from django.urls import reverse
from django.test import TestCase
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework_simplejwt.tokens import RefreshToken
from users.models import User
from circle.models import Post, Comment, Like, Category, Tag, PostTag, PostImage


class CircleModelTest(TestCase):
    """测试圈子模块的模型"""

    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            password='testpassword',
            email='test@example.com'
        )
        self.category = Category.objects.create(
            name='测试分类',
            description='这是一个测试分类'
        )

    def test_post_creation(self):
        """测试帖子创建"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category,
            post_type='second_hand',
            allow_comment=True
        )
        self.assertEqual(post.title, '测试帖子')
        self.assertEqual(post.author, self.user)
        self.assertEqual(str(post), '测试帖子')
        self.assertTrue(post.allow_comment)
        self.assertEqual(post.post_type, 'second_hand')

    def test_post_with_image(self):
        """测试带有图片的帖子创建"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        # 使用临时文件路径进行测试
        image = PostImage.objects.create(
            post=post,
            image='post_images/test.jpg'
        )
        self.assertEqual(image.post, post)
        self.assertEqual(post.images.count(), 1)

    def test_comment_creation(self):
        """测试评论创建"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        comment = Comment.objects.create(
            post=post,
            author=self.user,
            content='这是一个测试评论'
        )
        self.assertEqual(comment.content, '这是一个测试评论')
        self.assertEqual(comment.post, post)
        self.assertEqual(comment.author, self.user)

    def test_comment_reply(self):
        """测试评论回复功能"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        parent_comment = Comment.objects.create(
            post=post,
            author=self.user,
            content='这是一个测试评论'
        )
        reply_comment = Comment.objects.create(
            post=post,
            author=self.user,
            content='这是一个回复评论',
            parent=parent_comment
        )
        self.assertEqual(reply_comment.parent, parent_comment)
        self.assertEqual(parent_comment.replies.count(), 1)

    def test_like_creation(self):
        """测试点赞创建"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        like = Like.objects.create(
            post=post,
            user=self.user
        )
        self.assertEqual(like.post, post)
        self.assertEqual(like.user, self.user)
        
    def test_comment_like(self):
        """测试评论点赞"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        comment = Comment.objects.create(
            post=post,
            author=self.user,
            content='这是一个测试评论'
        )
        like = Like.objects.create(
            comment=comment,
            user=self.user
        )
        self.assertEqual(like.comment, comment)
        self.assertEqual(like.user, self.user)

    def test_tag_and_post_tag(self):
        """测试标签和帖子标签关联"""
        post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category
        )
        tag = Tag.objects.create(name='测试标签')
        post_tag = PostTag.objects.create(post=post, tag=tag)
        
        self.assertEqual(post_tag.post, post)
        self.assertEqual(post_tag.tag, tag)
        self.assertIn(tag, post.tags)


class CircleAPITest(APITestCase):
    """测试圈子模块的API"""

    def setUp(self):
        """设置测试数据"""
        self.user = User.objects.create_user(
            username='testuser',
            password='testpassword',
            email='test@example.com'
        )
        self.category = Category.objects.create(
            name='测试分类',
            description='这是一个测试分类'
        )
        self.tag = Tag.objects.create(name='测试标签')
        # 生成JWT令牌
        refresh = RefreshToken.for_user(self.user)
        self.token = str(refresh.access_token)
        # 设置认证头
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.token}')
        self.post = Post.objects.create(
            author=self.user,
            title='测试帖子',
            content='这是一个测试帖子的内容',
            category=self.category,
            post_type='second_hand',
            allow_comment=True
        )
        # 添加标签
        PostTag.objects.create(post=self.post, tag=self.tag)
        
    def test_category_list(self):
        """测试获取分类列表"""
        url = reverse('circle:category_list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('data', response.data)
        # 验证分类是否在响应中
        categories = response.data['data']
        category_names = [cat['name'] for cat in categories]
        self.assertIn('测试分类', category_names)

    def test_tag_list(self):
        """测试获取标签列表"""
        url = reverse('circle:tag_list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('data', response.data)
        # 验证标签是否在响应中
        tags = response.data['data']
        tag_names = [t['name'] for t in tags]
        self.assertIn('测试标签', tag_names)

    def test_post_list(self):
        """测试获取帖子列表"""
        url = reverse('circle:post_list')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 检查响应结构
        self.assertIn('data', response.data)
        self.assertIn('list', response.data['data'])
        # 筛选当前用户创建的帖子
        user_posts = [post for post in response.data['data']['list'] if post['author'] == self.user.id]
        self.assertEqual(len(user_posts), 1)

    def test_post_detail(self):
        """测试获取帖子详情"""
        url = reverse('circle:post_detail', kwargs={'pk': self.post.id})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 检查响应数据结构并获取title
        self.assertIn('data', response.data)
        self.assertIn('title', response.data['data'])
        self.assertEqual(response.data['data']['title'], '测试帖子')
        # 检查allow_comment字段是否在响应中
        self.assertIn('allow_comment', response.data['data'])
        
    def test_create_post(self):
        """测试创建帖子"""
        url = reverse('circle:post_create')
        data = {
            'title': '新测试帖子',
            'content': '这是一个新的测试帖子内容',
            'category': self.category.id,
            'post_type': 'dating',
            'allow_comment': False,
            'tag_ids': [self.tag.id]  # 使用正确的字段名
        }
        response = self.client.post(url, data)
        # 创建帖子应返回200，因为视图使用了自定义的success_response
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(Post.objects.count(), 2)
        # 验证新创建的帖子是否包含allow_comment=False
        new_post = Post.objects.exclude(id=self.post.id).first()
        self.assertFalse(new_post.allow_comment)
        self.assertEqual(new_post.post_type, 'dating')
        # 验证标签是否关联
        self.assertEqual(new_post.post_tags.count(), 1)

    def test_update_post(self):
        """测试更新帖子"""
        url = reverse('circle:post_update', kwargs={'pk': self.post.id})
        data = {
            'title': '更新后的测试帖子',
            'content': '这是更新后的测试帖子内容',
            'category': self.category.id,
            'allow_comment': False,
            'post_type': 'second_hand'  # 添加必要的post_type字段
        }
        response = self.client.patch(url, data)  # 使用patch而不是put
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 刷新帖子对象并验证更新
        self.post.refresh_from_db()
        self.assertEqual(self.post.title, '更新后的测试帖子')
        self.assertFalse(self.post.allow_comment)

    def test_delete_post(self):
        """测试删除帖子"""
        url = reverse('circle:post_delete', kwargs={'pk': self.post.id})
        response = self.client.delete(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        # 刷新帖子对象并验证状态
        self.post.refresh_from_db()
        self.assertEqual(self.post.status, 'deleted')

    def test_create_comment(self):
        """测试添加评论"""
        url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        data = {
            'content': '这是一个新的测试评论'
        }
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(Comment.objects.count(), 1)

    def test_create_comment_on_disabled_post(self):
        """测试在不允许评论的帖子上添加评论"""
        # 创建一个不允许评论的帖子
        no_comment_post = Post.objects.create(
            author=self.user,
            title='不允许评论的帖子',
            content='这是一个不允许评论的帖子',
            category=self.category,
            allow_comment=False
        )
        url = reverse('circle:comment_list', kwargs={'post_id': no_comment_post.id})
        data = {
            'content': '尝试在不允许评论的帖子上添加评论'
        }
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        # 验证没有创建评论
        self.assertEqual(Comment.objects.filter(post=no_comment_post).count(), 0)

    def test_comment_reply(self):
        """测试评论回复"""
        # 先创建一个评论
        comment_url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        comment_data = {'content': '这是一个测试评论'}
        comment_response = self.client.post(comment_url, comment_data)
        self.assertEqual(comment_response.status_code, status.HTTP_200_OK)
        
        comment_id = comment_response.data['data']['id']
        # 创建一个回复评论（不是更新原评论）
        reply_url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        reply_data = {
            'content': '这是一个回复',
            'parent': comment_id
        }
        reply_response = self.client.post(reply_url, reply_data)
        self.assertEqual(reply_response.status_code, status.HTTP_200_OK)
        
        # 验证回复是否创建
        comment = Comment.objects.get(id=comment_id)
        self.assertEqual(comment.replies.count(), 1)

    def test_update_comment(self):
        """测试更新评论"""
        # 先创建一个评论
        comment_url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        comment_data = {'content': '这是一个测试评论'}
        comment_response = self.client.post(comment_url, comment_data)
        self.assertEqual(comment_response.status_code, status.HTTP_200_OK)
        
        comment_id = comment_response.data['data']['id']
        # 更新该评论 - 使用 patch 方法而不是 put
        update_url = reverse('circle:comment_detail', kwargs={'pk': comment_id})
        update_data = {'content': '这是更新后的评论内容'}
        update_response = self.client.patch(update_url, update_data)
        self.assertEqual(update_response.status_code, status.HTTP_200_OK)
        
        # 验证评论是否更新
        comment = Comment.objects.get(id=comment_id)
        self.assertEqual(comment.content, '这是更新后的评论内容')

    def test_delete_comment(self):
        """测试删除评论"""
        # 先创建一个评论
        comment_url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        comment_data = {'content': '这是一个测试评论'}
        comment_response = self.client.post(comment_url, comment_data)
        self.assertEqual(comment_response.status_code, status.HTTP_200_OK)
        
        comment_id = comment_response.data['data']['id']
        # 删除该评论
        delete_url = reverse('circle:comment_detail', kwargs={'pk': comment_id})
        delete_response = self.client.delete(delete_url)
        self.assertEqual(delete_response.status_code, status.HTTP_200_OK)
        
        # 验证评论是否被隐藏
        comment = Comment.objects.get(id=comment_id)
        self.assertTrue(comment.is_hidden)

    def test_like_post(self):
        """测试点赞帖子"""
        url = reverse('circle:like')
        data = {
            'post': self.post.id
        }
        response = self.client.post(url, data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(Like.objects.count(), 1)

    def test_unlike_post(self):
        """测试取消点赞"""
        url = reverse('circle:like')
        data = {'post': self.post.id}

        # 确保没有已存在的点赞
        self.assertEqual(Like.objects.count(), 0)

        # 先点赞帖子
        like_response = self.client.post(url, data)
        self.assertEqual(like_response.status_code, status.HTTP_200_OK)
        self.assertEqual(Like.objects.count(), 1)

        # 再取消点赞
        response = self.client.delete(url, data=data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(Like.objects.count(), 0)

    def test_like_comment(self):
        """测试点赞评论"""
        # 先创建一个评论
        comment_url = reverse('circle:comment_list', kwargs={'post_id': self.post.id})
        comment_data = {'content': '这是一个测试评论'}
        comment_response = self.client.post(comment_url, comment_data)
        self.assertEqual(comment_response.status_code, status.HTTP_200_OK)
        
        comment_id = comment_response.data['data']['id']
        # 点赞该评论
        like_url = reverse('circle:like')
        like_data = {'comment': comment_id}
        like_response = self.client.post(like_url, like_data)
        self.assertEqual(like_response.status_code, status.HTTP_200_OK)
        self.assertEqual(Like.objects.count(), 1)
        
    def test_post_search(self):
        """测试帖子搜索功能"""
        url = reverse('circle:post_search')
        data = {
            'keyword': '测试'
        }
        response = self.client.get(url, data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('data', response.data)
        self.assertIn('list', response.data['data'])
        # 验证搜索结果包含相关帖子
        self.assertGreater(len(response.data['data']['list']), 0)

    def test_user_posts(self):
        """测试获取指定用户的帖子"""
        url = reverse('circle:user_posts', kwargs={'user_id': self.user.id})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('data', response.data)
        self.assertIn('list', response.data['data'])
        # 验证返回了当前用户的帖子
        self.assertEqual(len(response.data['data']['list']), 1)

    def test_user_liked_posts(self):
        """测试获取用户点赞的帖子"""
        # 先点赞帖子
        like_url = reverse('circle:like')
        like_data = {'post': self.post.id}
        like_response = self.client.post(like_url, like_data)
        self.assertEqual(like_response.status_code, status.HTTP_200_OK)
        
        # 获取用户点赞的帖子
        url = reverse('circle:user_liked_posts')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('data', response.data)
        self.assertIn('list', response.data['data'])
        # 验证返回了用户点赞的帖子
        self.assertEqual(len(response.data['data']['list']), 1)